﻿using System;
using System.Collections.Generic;
using System.Net;
using System.Text.RegularExpressions;
using DullBoard.Helper;
using DullBoard.Model;
using HtmlAgilityPack;

namespace DullBoard.Deserializers
{
    public static class ForumDeserializer
    {

        public static List<Forum> DeserializeForums(this HtmlNode forumListNode)
        {
            var forums = new List<Forum>();
            var parents = new Dictionary<Int64, string>();

            var parentForums = forumListNode.SelectNodes(forumListNode.XPath + "//a[@class=\"forum-parent\"]");
            foreach (var parent in parentForums)
            {
                var parentId = Int64.Parse(parent.Attributes["id"].Value.Replace("forum", ""));
                var parentName = parent.InnerText.Trim().RemoveNonAscii();
                parents.Add(parentId,parentName);
            }

            var childForums = forumListNode.SelectNodes(forumListNode.XPath + "//div[@class=\"child-forums\"]");
            foreach (var child in childForums)
            {
                var childId = Int64.Parse(child.Attributes["id"].Value.Replace("child", ""));

                var children = child.SelectNodes(child.XPath + "//div[@class=\"forums-list\"]/a[@class=\"forum-link\"]");
                foreach (var f in children)
                {

                    var forum = new Forum();
                    forum.Id = Int64.Parse(f.Attributes["href"].Value.Replace("/", ""));
                    forum.Name = f.SelectSingleNode(f.XPath + "//span[@class=\"int-padding\"]").ChildNodes[0].InnerText.Trim().RemoveNonAscii();
                    forum.Description = f.SelectSingleNode(f.XPath + "//span[@class=\"int-padding\"]/span[@class=\"desc\"]").InnerText.Trim().RemoveNonAscii();
                    forum.CategoryId = childId;
                    forum.CategoryName = parents[childId];
                    forums.Add(forum);
                }

            }


            return forums;
        }


        public static Topic DeserializeTopic(this HtmlNode topicNode)
        {
            var forumTopic = new Topic();
            forumTopic.Id = Int64.Parse(topicNode.Attributes["id"].Value.Replace("postRow", ""));

            var blizzardIcon = topicNode.SelectSingleNode(topicNode.XPath + XPathConst.ForumTopidBlizzardIcon);

            if (blizzardIcon != null)
            {
                forumTopic.ContainsBlizzardPost = true;
                forumTopic.BlizzardPost = blizzardIcon.Attributes["href"].Value.EndsWith("#1");
            }

            var containsPages = topicNode.SelectNodes(topicNode.XPath + XPathConst.ForumTopicPageCount);
            HtmlNode pageCount = null;
            if (containsPages != null)
            {
                var pageElementCount = topicNode.SelectNodes(topicNode.XPath + XPathConst.ForumTopicPageCount).Count;
                var pageCountNode =
                    topicNode.SelectNodes(topicNode.XPath + XPathConst.ForumTopicPageCount)[pageElementCount - 1];
                pageCount = pageCountNode.SelectSingleNode(pageCountNode.XPath + "//a[1]/span[1]");
            }
            forumTopic.PageCount = pageCount == null ? 1 : Int32.Parse(pageCount.InnerText);

            var stickySpan = topicNode.SelectSingleNode(topicNode.XPath + XPathConst.ForumTopicSticky);
            if (stickySpan != null)
                forumTopic.Sticky = topicNode.SelectSingleNode(topicNode.XPath + XPathConst.ForumTopicSticky).InnerText.Contains("[Sticky]");

            if (forumTopic.BlizzardPost)
                forumTopic.Author = WebUtility.HtmlDecode(topicNode.SelectSingleNode(topicNode.XPath + XPathConst.ForumTopicBlizzardAuthor).InnerText.Trim());
            else
                forumTopic.Author = WebUtility.HtmlDecode(topicNode.SelectSingleNode(topicNode.XPath + XPathConst.ForumTopicAuthor).InnerText.Trim());

            forumTopic.ReplyCount = Int32.Parse(topicNode.SelectSingleNode(topicNode.XPath + XPathConst.ForumTopicReplyCount).InnerText);
            forumTopic.ViewCount = Int32.Parse(topicNode.SelectSingleNode(topicNode.XPath + XPathConst.ForumTopicViewCount).InnerText);
            forumTopic.LastPostBy = WebUtility.HtmlDecode(topicNode.SelectSingleNode(topicNode.XPath + XPathConst.ForumTopicLastPostBy).InnerText.Trim());
            forumTopic.Title = WebUtility.HtmlDecode(topicNode.SelectSingleNode(topicNode.XPath + XPathConst.ForumTopicPostTitle).InnerText.Trim().RemoveNonAscii());
            forumTopic.Summary = WebUtility.HtmlDecode(topicNode.SelectSingleNode(topicNode.XPath + XPathConst.ForumTopicPostSummary).InnerText.Trim().RemoveNonAscii());
            forumTopic.DatePosted = topicNode.SelectSingleNode(topicNode.XPath + XPathConst.ForumTopicPostDate).InnerText.DeserializeDate();

            return forumTopic;
        }

        public static DateTime DeserializeDate(this string dateElement)
        {
            DateTime date;
            var result = DateTime.TryParse(dateElement, out date);
            if (result) return date;

            dateElement = dateElement.RemoveNonAscii();

            // 9/29/2012 3:21 PM PDT
            var match = Regex.Match(dateElement, "(?<Month>[0-9]+)/(?<Day>[0-9]+)/(?<Year>[0-9]+) (?<Hour>[0-9]+):(?<Minute>[0-9]+) (?<AMPM>[A-Z]+) (?<TimeZone>[A-Z]+)");
            if (match.Success)
            {
                var year = Int32.Parse(match.Groups["Year"].ToString());
                var month = Int32.Parse(match.Groups["Month"].ToString());
                var day = Int32.Parse(match.Groups["Day"].ToString());
                var hour = Int32.Parse(match.Groups["Hour"].ToString());
                var minute = Int32.Parse(match.Groups["Minute"].ToString());
                var AmPm = match.Groups["AMPM"].ToString();
                var timeZone = match.Groups["TimeZone"].ToString();

                if (AmPm == "PM")hour += 12;

                date = new DateTime(year,month,day,hour,minute,0);
                var timeZoneInfo = TimeZoneInfo.FindSystemTimeZoneById("Pacific Standard Time");
                date = TimeZoneInfo.ConvertTimeToUtc(date, timeZoneInfo);

            }

            match = Regex.Match(dateElement, "(?<Day>[0-9]+)/(?<Month>[0-9]+)/(?<Year>[0-9]+) (?<Hour>[0-9]+):(?<Minute>[0-9]+)(?<TimeZone>[A-Z]+)");
            if (match.Success)
            {
                var year = Int32.Parse(match.Groups["Year"].ToString());
                var month = Int32.Parse(match.Groups["Month"].ToString());
                var day = Int32.Parse(match.Groups["Day"].ToString());
                var hour = Int32.Parse(match.Groups["Hour"].ToString());
                var minute = Int32.Parse(match.Groups["Minute"].ToString());
                var AmPm = match.Groups["AMPM"].ToString();
                var timeZone = match.Groups["TimeZone"].ToString();

                if (AmPm == "PM") hour += 12;

                date = new DateTime(year, month, day, hour, minute, 0);
                var timeZoneInfo = TimeZoneInfo.FindSystemTimeZoneById("GMT Standard Time");
                date = TimeZoneInfo.ConvertTimeToUtc(date, timeZoneInfo);

            }


            match = Regex.Match(dateElement, "(?<Days>[0-9]+) day.* ago");
            if (match.Success)
            {
                var days = Int32.Parse(match.Groups["Days"].ToString());

                date = DateTime.Now.Subtract(new TimeSpan(days, 0, 0));
                return date;
            }

            match = Regex.Match(dateElement, "(?<Days>[0-9]+) day.*, (?<Hours>[0-9]+) hours ago");
            if (match.Success)
            {
                var days = Int32.Parse(match.Groups["Days"].ToString());
                var hours = Int32.Parse(match.Groups["Hours"].ToString());

                date = DateTime.Now.Subtract(new TimeSpan(days, hours, 0));
                return date;
            }

            match = Regex.Match(dateElement, "(?<Hours>[0-9]+) hour.* ago");
            if (match.Success)
            {
                var hours = Int32.Parse(match.Groups["Hours"].ToString());

                date = DateTime.Now.Subtract(new TimeSpan(0,hours, 0, 0));
                return date;
            }

            match = Regex.Match(dateElement, "(?<Hours>[0-9]+) hours, (?<Minutes>[0-9]+) minutes ago");
            if (match.Success)
            {
                var hours = Int32.Parse(match.Groups["Hours"].ToString());
                var minutes = Int32.Parse(match.Groups["Minutes"].ToString());

                date = DateTime.Now.Subtract(new TimeSpan(0, hours, minutes));
                return date;
            }

            match = Regex.Match(dateElement, "(?<Minutes>[0-9]+) minute.* ago");
            if (match.Success)
            {
                var minutes = Int32.Parse(match.Groups["Minutes"].ToString());

                date = DateTime.Now.Subtract(new TimeSpan(0, 0, minutes,0));
                return date;
            }


            if (date < new DateTime(1970,1,1)) throw new ApplicationException(dateElement + " does not match RegEx expression");

            return date;
        }
    }

}
